﻿using System;
using System.Collections.Generic;

namespace UtilZ.Dotnet.Ex.Base
{
    /// <summary>
    /// 字典集合扩展类
    /// </summary>
    public static class DictionaryEx
    {
        /// <summary>
        /// ConcurrentDictionary字典添加项[返回添加结果]
        /// </summary>
        /// <typeparam name="T">key类型</typeparam>
        /// <typeparam name="W">value类型</typeparam>
        /// <param name="dic">ConcurrentDictionary字典</param>
        /// <param name="key">key</param>
        /// <param name="value">value</param>
        /// <param name="repeatCount">添加失败重试次数</param>
        /// <returns>返回添加结果</returns>
        public static bool Add<T, W>(this System.Collections.Concurrent.ConcurrentDictionary<T, W> dic, T key, W value, int repeatCount = 0)
        {
            int currentRepeatCount = 0;
            bool ret = dic.TryAdd(key, value);
            while (!ret && currentRepeatCount++ < repeatCount)
            {
                System.Threading.Thread.Sleep(10);
                ret = dic.TryAdd(key, value);
            }

            return ret;
        }

        /// <summary>
        /// 根据指定的键选择器函数，从 System.Collections.Generic.IEnumerable创建一个 System.Collections.Generic.Dictionary
        /// </summary>
        /// <typeparam name="TSource">source 中的元素的类型</typeparam>
        /// <typeparam name="TKey">keySelector 返回的键的类型</typeparam>
        /// <param name="source">一个 System.Collections.Generic.IEnumerable，将从它创建一个 System.Collections.Generic.Dictionary</param>
        /// <param name="keySelector">用于从每个元素中提取键的函数</param>
        /// <param name="distinctSelector">用于筛选重复元素值的转换函数</param>
        /// <returns>一个包含键和值的 System.Collections.Generic.Dictionary</returns>
        public static Dictionary<TKey, TSource> ToDictionary<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector,
            Func<TKey, TSource, TSource, TSource> distinctSelector = null)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }

            if (keySelector == null)
            {
                throw new ArgumentNullException(nameof(keySelector));
            }

            var dic = new Dictionary<TKey, TSource>();
            TKey key;
            foreach (var item in source)
            {
                if (item == null)
                {
                    continue;
                }

                key = keySelector(item);
                if (key == null)
                {
                    continue;
                }

                if (dic.ContainsKey(key))
                {
                    if (distinctSelector != null)
                    {
                        dic[key] = distinctSelector(key, dic[key], item);
                    }
                    else
                    {
                        throw new ArgumentException($"{nameof(keySelector)}为两个元素产生了重复键\"{key}\"");
                    }
                }
                else
                {
                    dic.Add(key, item);
                }
            }

            return dic;
        }

        /// <summary>
        /// 根据指定的键选择器函数，从 System.Collections.Generic.IEnumerable创建一个 System.Collections.Generic.Dictionary
        /// </summary>
        /// <typeparam name="TSource">source 中的元素的类型</typeparam>
        /// <typeparam name="TKey">keySelector 返回的键的类型</typeparam>
        /// <typeparam name="TElement">elementSelector 返回的值的类型</typeparam>
        /// <param name="source">一个 System.Collections.Generic.IEnumerable，将从它创建一个 System.Collections.Generic.Dictionary</param>
        /// <param name="keySelector">用于从每个元素中提取键的函数</param>
        /// <param name="elementSelector">用于从每个元素产生结果元素值的转换函数</param>
        /// <param name="distinctSelector">用于筛选重复元素值的转换函数</param>
        /// <returns>一个包含键和值的 System.Collections.Generic.Dictionary</returns>
        public static Dictionary<TKey, TElement> ToDictionary<TSource, TKey, TElement>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector,
            Func<TSource, TElement> elementSelector, Func<TKey, TElement, TElement, TElement> distinctSelector = null)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }

            if (keySelector == null)
            {
                throw new ArgumentNullException(nameof(keySelector));
            }

            if (elementSelector == null)
            {
                throw new ArgumentNullException(nameof(elementSelector));
            }

            var dic = new Dictionary<TKey, TElement>();
            TKey key;
            TElement value;
            foreach (var item in source)
            {
                if (item == null)
                {
                    continue;
                }

                key = keySelector(item);
                if (key == null)
                {
                    continue;
                }

                value = elementSelector(item);

                if (dic.ContainsKey(key))
                {
                    if (distinctSelector != null)
                    {
                        dic[key] = distinctSelector(key, dic[key], value);
                    }
                    else
                    {
                        throw new ArgumentException($"{nameof(keySelector)}为两个元素产生了重复键\"{key}\"");
                    }
                }
                else
                {
                    dic.Add(key, value);
                }
            }

            return dic;
        }
    }
}
